package com.learn.security.utils.symmetric_encryption;

import com.learn.security.utils.BaseUtil;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.SecureRandom;

/**
 * AES加密工具类
 * AES、Blowfish
 * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
 * Blowfish             key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
 */
public class AESUtil extends BaseUtil {

    /**
     * ALGORITHM 算法
     * 可替换为以下任意一种算法，同时key值的size相应改变
     * AES                  key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available
     * Blowfish             key size must be multiple of 8, and can only range from 32 to 448 (inclusive)
     * <p>
     * 替换为对应的加密方式即可
     */
    public static final String ALGORITHM = "AES";

    /**
     * 生成密钥
     *
     * @return 密钥--base64加密的字符
     */
    public static String initKey() throws Exception {
        return initKey(null);
    }

    /**
     * 生成密钥
     *
     * @param seed the seed
     * @return 密钥--base64加密的字符
     */
    public static String initKey(String seed) throws Exception {
        SecureRandom secureRandom = getSecureRandomForAES(seed);

        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
        kg.init(secureRandom);

        SecretKey secretKey = kg.generateKey();

        return encryptBASE64(secretKey.getEncoded());
    }

    /**
     * 根据获取SecureRandom
     *
     * @param seed the seed
     */
    private static SecureRandom getSecureRandomForAES(String seed) throws Exception {
        SecureRandom secureRandom;

        if (seed != null) {
            secureRandom = new SecureRandom(decryptBASE64(seed));
        } else {
            secureRandom = new SecureRandom();
        }
        return secureRandom;
    }

    /**
     * 转换密钥<br>
     *
     * @param key 秘钥--base64加密的字符
     * @return 转化后秘钥
     */
    private static Key toKey(byte[] key) throws Exception {

        //当使用其他对称加密算法时，如AES、Blowfish等算法时，用下述代码替换上述三行代码
        SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);

        return secretKey;
    }

    /**
     * 加密
     *
     * @param data 加密文件
     * @param key  密钥--base64加密的字符
     */
    public static byte[] encrypt(byte[] data, String key) throws Exception {
        Key k = toKey(decryptBASE64(key));

        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, k);

        return cipher.doFinal(data);
    }

    /**
     * 解密
     *
     * @param data 加密文件
     * @param key  密钥--base64加密的字符
     */
    public static byte[] decrypt(byte[] data, String key) throws Exception {
        Key k = toKey(decryptBASE64(key));

        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, k);

        return cipher.doFinal(data);
    }

}
